// filehdr.cc 
//	Routines for managing the disk file header (in UNIX, this
//	would be called the i-node).
//
//	The file header is used to locate where on disk the 
//	file's data is stored.  We implement this as a fixed size
//	table of pointers -- each entry in the table points to the 
//	disk sector containing that portion of the file data
//	(in other words, there are no indirect or doubly indirect 
//	blocks). The table size is chosen so that the file header
//	will be just big enough to fit in one disk sector, 
//
//      Unlike in a real system, we do not keep track of file permissions, 
//	ownership, last modification date, etc., in the file header. 
//
//	A file header can be initialized in two ways:
//	   for a new file, by modifying the in-memory data structure
//	     to point to the newly allocated data blocks
//	   for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "filehdr.h"
#include "debug.h"
#include "synchdisk.h"
#include "main.h"

#define NumDirectIndexSectors    25
#define NumFirstIndexSectors    (NumDirectIndexSectors + 32)
#define NumSecondIndexSectors   (NumFirstIndexSectors + 32*32)
#define FirstIndexSector         25
#define SecondIndexSector        26


//----------------------------------------------------------------------
// FileHeader::Allocate
// 	Initialize a fresh file header for a newly created file.
//	Allocate data blocks for the file out of the map of free disk blocks.
//	Return FALSE if there are not enough free blocks to accomodate
//	the new file.
//
//	"freeMap" is the bit map of free disk sectors
//	"fileSize" is the bit map of free disk sectors
//----------------------------------------------------------------------

bool FileHeader::Allocate(PersistentBitmap *freeMap, int fileSize) {
	numBytes = fileSize;
	numSectors = divRoundUp(fileSize, SectorSize);
	if (freeMap->NumClear() < numSectors)
		return FALSE; // not enough space
		//edit
	SetCreateTime(time(0));
	SetRecentTime(time(0));

	int t = (numSectors <= NumDirectIndexSectors) ? numSectors : NumDirectIndexSectors;
	for (int i = 0; i < t; i++) {
		dataSectors[i] = freeMap->FindAndSet();
		ASSERT(dataSectors[i] >= 0);
	}
	if (numSectors > NumDirectIndexSectors) {
		t = (numSectors <= NumFirstIndexSectors) ? numSectors : NumFirstIndexSectors;
		dataSectors[FirstIndexSector] = freeMap->FindAndSet();
		ASSERT(dataSectors[FirstIndexSector] >= 0);
		int* temp1 = new int[32];
		for (int i = NumDirectIndexSectors; i < t; i++) {
			temp1[i - NumDirectIndexSectors] = freeMap->FindAndSet();
			ASSERT(temp1[i-NumDirectIndexSectors] >= 0);
		}
		kernel->synchDisk->WriteSector(dataSectors[FirstIndexSector], (char *) temp1);
		delete[] temp1;
	}
	if (numSectors > NumFirstIndexSectors) {
		dataSectors[SecondIndexSector] = freeMap->FindAndSet();
		ASSERT(dataSectors[SecondIndexSector] >= 0);
		int* temp1 = new int[32];
		int count = NumFirstIndexSectors, i = 0, j = 0; //"count" is used for counting the number of allocated datasector
		while (count < numSectors && i < 32) {
			temp1[i] = freeMap->FindAndSet();
			ASSERT(temp1[i] >= 0);
			int* temp2 = new int[32];
			while (count < numSectors && j < 32) {
				temp2[j] = freeMap->FindAndSet();
				ASSERT(temp2[j] >= 0);
				j++;
				count++;
			}
			kernel->synchDisk->WriteSector(temp1[i], (char *) temp2);
			delete[] temp2;
			i++;
			j = 0;
		}
		kernel->synchDisk->WriteSector(dataSectors[SecondIndexSector], (char *) temp1);
		delete[] temp1;
	}
	return TRUE;
}

bool FileHeader::ReAllocate(PersistentBitmap *freeMap, int fileSize) {
	this->Deallocate(freeMap);
	return this->Allocate(freeMap, fileSize);
}
//----------------------------------------------------------------------
// FileHeader::Deallocate
// 	De-allocate all the space allocated for data blocks for this file.
//
//	"freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------

void FileHeader::Deallocate(PersistentBitmap *freeMap) {
	int t = (numSectors <= NumDirectIndexSectors) ? numSectors : NumDirectIndexSectors;
	for (int i = 0; i < t; i++) {
		ASSERT(freeMap->Test((int) dataSectors[i]));
		freeMap->Clear((int) dataSectors[i]);
	}
	if (numSectors > NumDirectIndexSectors) {
		t = (numSectors <= NumFirstIndexSectors) ? numSectors : NumFirstIndexSectors;
		int* temp1 = new int[32];
		kernel->synchDisk->ReadSector(dataSectors[FirstIndexSector], (char *) temp1);
		for (int i = NumDirectIndexSectors; i < t; i++) {
			ASSERT(freeMap->Test((int) temp1[i-NumDirectIndexSectors]));
			freeMap->Clear((int) temp1[i - NumDirectIndexSectors]);
		}
		ASSERT(freeMap->Test((int) dataSectors[FirstIndexSector]));
		freeMap->Clear((int) dataSectors[FirstIndexSector]);
		delete[] temp1;
	}
	if (numSectors > NumFirstIndexSectors) {
		int* temp1 = new int[32];
		int count = NumFirstIndexSectors, i = 0, j = 0;
		kernel->synchDisk->ReadSector(dataSectors[SecondIndexSector], (char *) temp1);
		while (count < numSectors && i < 32) {
			int* temp2 = new int[32];
			kernel->synchDisk->ReadSector(temp1[i], (char *) temp2);
			while (count < numSectors && j < 32) {
				ASSERT(freeMap->Test((int) temp2[j]));
				freeMap->Clear((int) temp2[j]);
				j++;
				count++;
			}
			ASSERT(freeMap->Test((int) temp1[i]));
			freeMap->Clear((int) temp1[i]);
			delete[] temp2;
			i++;
			j = 0;
		}
		ASSERT(freeMap->Test((int) dataSectors[SecondIndexSector]));
		freeMap->Clear((int) dataSectors[SecondIndexSector]);
		delete[] temp1;
	}
}

//----------------------------------------------------------------------
// FileHeader::FetchFrom
// 	Fetch contents of file header from disk. 
//
//	"sector" is the disk sector containing the file header
//----------------------------------------------------------------------

void FileHeader::FetchFrom(int sector) {
	kernel->synchDisk->ReadSector(sector, (char *) this);
}

//----------------------------------------------------------------------
// FileHeader::WriteBack
// 	Write the modified contents of the file header back to disk. 
//
//	"sector" is the disk sector to contain the file header
//----------------------------------------------------------------------

void FileHeader::WriteBack(int sector) {
	kernel->synchDisk->WriteSector(sector, (char *) this);
}

//----------------------------------------------------------------------
// FileHeader::ByteToSector
// 	Return which disk sector is storing a particular byte within the file.
//      This is essentially a translation from a virtual address (the
//	offset in the file) to a physical address (the sector where the
//	data at the offset is stored).
//
//	"offset" is the location within the file of the byte in question
//----------------------------------------------------------------------

int FileHeader::ByteToSector(int offset) {
	//edit
	int i = offset / SectorSize;
	if (i < NumDirectIndexSectors)
		return (dataSectors[i]);
	else if (i >= NumDirectIndexSectors && i < NumFirstIndexSectors) {
		int* temp1 = new int[32];
		kernel->synchDisk->ReadSector(dataSectors[FirstIndexSector], (char *) temp1);
		return temp1[i - NumDirectIndexSectors];
	} else if (i >= NumFirstIndexSectors) {
		int* temp1 = new int[32];
		kernel->synchDisk->ReadSector(dataSectors[SecondIndexSector], (char *) temp1);
		int index1 = (i - NumFirstIndexSectors) / 32;
		int index2 = (i - NumFirstIndexSectors) % 32;
		int* temp2 = new int[32];
		kernel->synchDisk->ReadSector(temp1[index1], (char *) temp2);
		delete[] temp1;
		return temp2[index2];
	}
}

//----------------------------------------------------------------------
// FileHeader::FileLength
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int FileHeader::FileLength() {
	return numBytes;
}

//----------------------------------------------------------------------
// FileHeader::Print
// 	Print the contents of the file header, and the contents of all
//	the data blocks pointed to by the file header.
//----------------------------------------------------------------------

void FileHeader::Print() {//edit
	int i, j, k, m;
    char *data = new char[SectorSize];

	printf("File Type: %s.", FileType);
	printf(
			"FileHeader contents.  File size: %d.  numSectors:%d.  File blocks:\n",
			numBytes, numSectors);
	int t = (numSectors <= NumDirectIndexSectors ? numSectors : NumDirectIndexSectors);
	printf("Direct index:");
	for (i = 0; i < t; i++) {
		printf("%d ", dataSectors[i]);
	}
	printf("\nDirect index File contents:\n");
	for (i = k = 0; i < t; i++) {
		kernel->synchDisk->ReadSector(dataSectors[i], data);
		for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
			if ('\040' <= data[j] && data[j] <= '\176') // isprint(data[j])
				printf("%c", data[j]);
			else
				printf("\\%x", (unsigned char) data[j]);
		}
		printf("\n");
	}
	if (numSectors > NumDirectIndexSectors) {
		t = (numSectors <= NumFirstIndexSectors) ? numSectors : NumFirstIndexSectors;
		int* temp1 = new int[32];
		kernel->synchDisk->ReadSector(dataSectors[FirstIndexSector], (char *) temp1);
		printf("\nFirst index:");
		for (i = NumDirectIndexSectors; i < t; i++)
			printf("%d ", temp1[i - NumDirectIndexSectors]);//edit
		printf("\nFirst index File contents:\n");
		for (i = NumDirectIndexSectors, k = 0; i < t; i++) {
			kernel->synchDisk->ReadSector(temp1[i-NumDirectIndexSectors], data);
			for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
				if ('\040' <= data[j] && data[j] <= '\176') // isprint(data[j])
					printf("%c", data[j]);
				else
					printf("\\%x", (unsigned char) data[j]);
			}
			printf("\n");
		}
		delete[] temp1;
	}
	if (numSectors > NumFirstIndexSectors) {
		int* temp1 = new int[32];
		int count = NumFirstIndexSectors, i = 0, j = 0;
		kernel->synchDisk->ReadSector(dataSectors[SecondIndexSector], (char *) temp1);
		while (count < numSectors && i < 32) {
			int* temp2 = new int[32];
			kernel->synchDisk->ReadSector(temp1[i], (char *) temp2);
			printf("\nSecond index[%d]:", i);
			while (count < numSectors && j < 32) {
				printf("%d ", temp2[j]);
				j++;
				count++;
			}
			count = NumFirstIndexSectors;
			j = 0;
			printf("\nSecond index[%d] File contents:\n", i);
			while (count < numSectors && j < 32) {
				kernel->synchDisk->ReadSector(temp2[j], data);
				for (m = 0; (m < SectorSize) && (k < numBytes); j++, k++) {
					if ('\040' <= data[m] && data[m] <= '\176') // isprint(data[j])
						printf("%c", data[m]);
					else
						printf("\\%x", (unsigned char) data[m]);
				}
				printf("\n");
				j++;
				count++;
			}
			delete[] temp2;
			i++;
			j = 0;
		}
		delete[] temp1;
	}

	printf("\nCreateTime: %s", ctime(&CreateTime));
	printf("RecentTime: %s", ctime(&RecentTime));
}
void FileHeader::PrintSectors(){
	int i, j, k, m;
	int t = (numSectors <= NumDirectIndexSectors ? numSectors : NumDirectIndexSectors);
	cout<<endl;
	printf("Direct index Sectors %d: ", t);
	for (i = 0; i < t; i++) {
		printf("%d ", dataSectors[i]);
	}
	cout<<endl;
	if (numSectors > NumDirectIndexSectors) {
			t = (numSectors <= NumFirstIndexSectors) ? numSectors : NumFirstIndexSectors;
			int* temp1 = new int[32];
			kernel->synchDisk->ReadSector(dataSectors[FirstIndexSector], (char *) temp1);
			printf("\nFirst index Sectors %d :", t-NumDirectIndexSectors);
			for (i = NumDirectIndexSectors; i < t; i++)
				printf("%d ", temp1[i - NumDirectIndexSectors]);
			delete[] temp1;
	}
	cout<<endl;
	if (numSectors > NumFirstIndexSectors) {
		int* temp1 = new int[32];
		int count = NumFirstIndexSectors, i = 0, j = 0;
		kernel->synchDisk->ReadSector(dataSectors[SecondIndexSector], (char *) temp1);
		printf("\nSecond index Sectors %d: ", numSectors -NumFirstIndexSectors);
		while (count < numSectors && i < 32) {
			int* temp2 = new int[32];
			kernel->synchDisk->ReadSector(temp1[i], (char *) temp2);
			printf("\nSecond index[%d] Sectors: ", i);
			while (count < numSectors && j < 32) {
				printf("%d ", temp2[j]);
				j++;
				count++;
			}
			delete[] temp2;
			j = 0;
			i++;
			cout<<endl;
		}
		cout<<endl;
		delete[] temp1;
	}
}
//edit
void FileHeader::SetFileType(char *name) {
	bool record = FALSE;
	int i = 0;
	char *p = name;
	while (*p != '\0') {
		if (*p == '.') {
			record = TRUE;
			p++;
			continue;
		}
		if (record && i < 3) {
			FileType[i] = *p;
			i++;
		}
		if (i == 3)
			break;
		p++;
	}
	FileType[3] = '\0';
}
